﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EnvDTE;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Shell.Design;
using System.ComponentModel.Design;
using Microsoft.Samples.LinqToCodeModel.Extensions;
using AutomationFoundation.CodeToolKit.VisualStudio.Linq;
using AutomationFoundation.CodeToolKit;
using Microsoft.Samples.LinqToCodeModel;

namespace MVC4WPF.Automation.Data
{
    /// <summary>
    /// Helper class that is used to search a projects code base and references to find all instances of a MVC4WPF propertybag.
    /// </summary>
    public static class PropertyBagFinder
    {
        /// <summary>
        /// Helper method that finds a list of property bags within the target project.
        /// </summary>
        /// <param name="solution">Reference to the runing solution.</param>
        /// <param name="loadedTypes">Dynamic type service reference that will allow you to search for the loaded property bags.</param>
        /// <param name="sourceProject">Reference to the project that is to be searched.</param>
        /// <returns>List of propertybag currently supported in the target project. If no property bags are found an empty list will be returned.</returns>
        public static List<PropertyBagDefinition> GetPropertyBags(IVsSolution solution, DynamicTypeService loadedTypes, Project sourceProject)
        {
            List<PropertyBagDefinition> compiledBags = GetCompiledPropertyBags(solution, loadedTypes, sourceProject);
            List<PropertyBagDefinition> codeBags = GetCodePropertyBags(sourceProject);
            List<PropertyBagDefinition> uniqueBags = compiledBags.Union(from code in codeBags
                                                                        where !(from compiled in compiledBags select compiled.FullName).Contains(code.FullName)
                                                                        select code).ToList();
            return uniqueBags;
        }


        /// <summary>
        /// Gets a list of PropertyBagDefintion objects that are either in referenced assemblies or have already been compiled in the project itself.
        /// </summary>
        /// <param name="solution">Reference to the runing solution.</param>
        /// <param name="loadedTypes">Dynamic type service reference that will allow you to search for the loaded property bags.</param>
        /// <param name="sourceProject">Reference to the project that is to be searched.</param>
        /// <returns>List of propertybag currently supported in the target project. If no property bags are found an empty list will be returned.</returns>
        private static List<PropertyBagDefinition> GetCompiledPropertyBags(IVsSolution solution, DynamicTypeService loadedTypes, Project sourceProject)
        {
            IVsHierarchy ivh = null;

            try
            {
                solution.GetProjectOfUniqueName(sourceProject.UniqueName, out ivh);
            }
            catch (System.Exception)
            {
                return new List<PropertyBagDefinition>();
            }

            if (ivh != null)
            {

                ITypeDiscoveryService typeTracker = loadedTypes.GetTypeDiscoveryService(ivh);

                IList<System.Type> data = typeTracker.GetTypes(typeof(MVC4WPF.PropertyBagBase), true) as List<System.Type>;

                var testResults = from d in data
                                  where d.Name != MVC4WPFConstants.PropertyBagBaseName
                                  select new PropertyBagDefinition { Name = d.Name, Namespace = d.Namespace, FullName = d.FullName };

                return testResults.ToList<PropertyBagDefinition>();

            }

            return new List<PropertyBagDefinition>();
        }

        /// <summary>
        /// Gets a list of property bag definitions based on all propertybags defined in the project.
        /// </summary>
        /// <param name="sourceProject">The project to search for propertybags in.</param>
        /// <returns>List of propertybag currently supported in the target project. If no property bags are found an empty list will be returned</returns>
        private static List<PropertyBagDefinition> GetCodePropertyBags(Project sourceProject)
        {
            if (sourceProject == null)
            {
                return new List<PropertyBagDefinition>();
            }

            ProjectItems data = sourceProject.ProjectItems;

            var codeClasses = QueryableProjectItems.GetElementsWhere<ProjectItem>(data, P => P.Kind == ProjectItemTypes.PhysicalFile);

            List<PropertyBagDefinition> bags = new List<PropertyBagDefinition>();

            foreach (var item in codeClasses)
            {
                if (item.FileCodeModel != null)
                {
                    var propertyBags = from I in item.FileCodeModel.GetIEnumerable<CodeClass>()
                                       where QueryableCodeModel.GetElements<CodeClass>(I.Bases).Count(BC => BC.Name == MVC4WPFConstants.PropertyBagBaseName) != 0
                                       select new PropertyBagDefinition { Name = I.Name, Namespace = I.Namespace.Name, FullName = string.Format("{0}.{1}", I.Namespace.Name, I.Name) };
                    bags.AddRange(propertyBags);
                }
            }

            return bags;
        }
    }
}
